/*
 * scheduler_o_n.cpp
 *
 *  Created on: 2011-5-19
 *      Author: chenl
 */
#include "scheduler_o_n.h"
#include "logengine/buffedlog.h"

CScheduler_O_N::CScheduler_O_N() :
	logEngine(GET_PSTHREADLOG_INSTANCE())
{

}

CScheduler_O_N::~CScheduler_O_N()
{

}

void CScheduler_O_N::schedule()
{
	CProcessMgt& processMgt = GET_PROCESSMGT_INSTANCE();
	CTask* pCurrentTask = processMgt.CurrentTask();
	if (unlikely(NULL == pCurrentTask))
	{
		logEngine.WriteLog(enmLogLevel_FATAL,
				"pCurrentTask is NULL in CScheduler_O_N::schedule()!");
		return;
	}

need_resched_back:
	logEngine.WriteLog(enmLogLevel_INFO, "Start scheduling(current=%d, current->counter=%d)!", pCurrentTask->task.pid, pCurrentTask->task.counter);
	CTask* pPrevTask = pCurrentTask;
	int counter = -1000;

	if (unlikely(pPrevTask->task.policy == SCHED_RR))
	{
		logEngine.WriteLog(enmLogLevel_INFO, "current(pid=%d)->policy==SCHED_RR!", pPrevTask->task.pid);
		if (!pPrevTask->task.counter)
		{
			pPrevTask->task.counter = NICE_TO_TICKS(pPrevTask->task.nice);
			processMgt.MoveTaskToRunQueueTail(*pPrevTask);
			logEngine.WriteLog(enmLogLevel_INFO, "current(pid=%d)->counter==0, recalculating...(counter=%d,nice=%d), move to tail!", pPrevTask->task.pid, pPrevTask->task.counter, pPrevTask->task.nice);
		}
	}

	switch (pPrevTask->task.state)
	{
	case TASK_INTERRUPTIBLE:
		logEngine.WriteLog(enmLogLevel_INFO, "current(pid=%d)->state==TASK_INTERRUPTIBLE", pPrevTask->task.pid);
		if (pPrevTask->SignalPending())
		{
			pPrevTask->task.state = TASK_RUNNING;
			break;
		}
	default:
		logEngine.WriteLog(enmLogLevel_INFO, "current(pid=%d)->state(%d)!=TASK_INTERRUPTIBLE, remove from runqueue!", pPrevTask->task.pid, pPrevTask->task.state);
		processMgt.DelTaskFromRunqueue(*pPrevTask);
	case TASK_RUNNING:
		;
	}
	pPrevTask->task.need_resched = 0;

	logEngine.WriteLog(enmLogLevel_INFO, "Set current(pid=%d)->need_resched as 0");

repeat_schedule:
	CTask* pNextTask = &CTask::InitTask();
	counter = -1000;
	logEngine.WriteLog(enmLogLevel_INFO, "Set next = current, prepare for switching");
	CTask* pTask = NULL;
	for_each_task(processMgt, RunQueue, pTask)
	{
		int weight = goodness(pTask->task);
		logEngine.WriteLog(enmLogLevel_DEBUG, "Scanning runqueue... task(pid=%d,counter=%d,nice=%d,policy=%d) weight=%d", pTask->task.pid, pTask->task.counter, pTask->task.nice, pTask->task.policy, weight);
		if (weight > counter)
		{
			counter = weight;
			pNextTask = pTask;
		}
	}

	if (0 == counter)
	{
		logEngine.WriteLog(enmLogLevel_INFO, "0==counter, recalculating task->counter on runqueue");
		for_each_task(processMgt, RunQueue, pTask)
		{
			pTask->task.counter = (pTask->task.counter >> 1) + NICE_TO_TICKS(
					pTask->task.nice);
			logEngine.WriteLog(enmLogLevel_DEBUG, "Scanning runqueue... task(pid=%d,nice=%d) counter=%d", pTask->task.pid, pTask->task.nice, pTask->task.counter);
		}
		logEngine.WriteLog(enmLogLevel_INFO, "Go for a rescheduling");
		goto repeat_schedule;
	}
	logEngine.WriteLog(enmLogLevel_INFO, "Pick task(pid=%d) as next", pNextTask->task.pid);

	if (pPrevTask == pNextTask)
	{
		pPrevTask->task.policy &= ~SCHED_YIELD;
		logEngine.WriteLog(enmLogLevel_INFO, "Oops! Picked the same task(pid=%d) as next, discard policy SCHED_YIELD and do no switching", pNextTask->task.pid);
		goto same_process;
	}
	logEngine.WriteLog(enmLogLevel_INFO, "Do switching! current=%d, prev=%d, next=%d", pCurrentTask->task.pid, pPrevTask->task.pid, pNextTask->task.pid);
	switch_to(*pPrevTask, *pNextTask);
	pCurrentTask = processMgt.CurrentTask();

same_process:
	if (pCurrentTask->task.need_resched)
	{
		logEngine.WriteLog(enmLogLevel_INFO, "current(%d)->need_resched is set, we have to do another scheduling", pCurrentTask->task.pid);
		goto need_resched_back;
	}
	logEngine.WriteLog(enmLogLevel_INFO, "Finish switching! current(pid=%d), prev(pid=%d), next(pid=%d)", pCurrentTask->task.pid, pPrevTask->task.pid, pNextTask->task.pid);

	return;
}
